package com.myzl.coal.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.myzl.coal.domain.RealTimeData;
import com.myzl.coal.mapper.RealTimeDataMapper;
import com.myzl.coal.mongo.component.MongoComponent;
import com.myzl.coal.mongo.domain.HistoryData;
import com.myzl.coal.service.ILEDScreenService;
import com.myzl.common.core.domain.entity.SysDictData;
import com.myzl.common.enums.DeviceCommandEnum;
import com.myzl.common.enums.LedScreenContent;
import com.myzl.common.enums.OperateModeEnum;
import com.myzl.common.utils.DictUtils;
import com.myzl.common.utils.SpecificDateUtil;
import com.myzl.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author cew
 * @date 2023年10月19日10:10:50
 */
@Slf4j(topic = "admin-user")
@Service
public class LEDScreenServiceImpl implements ILEDScreenService {

    @Autowired
    private MongoComponent mongoComponent;
    @Autowired
    private RealTimeDataMapper realTimeDataMapper;

    @Override
    public String getDisPlayData(Integer deviceId,LedScreenContent ledScreenContent) {
        String result = "";
        if (ObjUtil.isEmpty(ledScreenContent)){
            return result;
        }
        switch (ledScreenContent){
            case TITLE:
                result = LedScreenContent.TITLE.getInfo();
                break;
            case CURRENT:
            case SPEED:
                result = getCurrent(ledScreenContent,deviceId);
                break;
            case MORNING:
            case NOON:
            case NIGHT:
                result = handleClassInfo(ledScreenContent,deviceId);
                break;
            case DAY:
            case MONTH:
            case YEAR:
            case ALL:
                result = getResultByContent(deviceId,ledScreenContent);
                break;
            default:
                break;
        }
        return result;
    }

    private String getCurrent(LedScreenContent ledScreenContent,Integer deviceId){
        String result = "";
        List<Integer> dictValues = buildDictValues();
        List<RealTimeData> currentRealTimeDataList = realTimeDataMapper.selectList(new LambdaQueryWrapper<RealTimeData>()
                .eq(RealTimeData::getDeviceId, deviceId)
                .in(RealTimeData::getDictValue, dictValues));
        Map<Integer,String> currentRealTimeMap = currentRealTimeDataList.stream()
                .collect(Collectors.toMap(RealTimeData::getDictValue,RealTimeData::getDataValue));
        // 当前带速模式
        String operateMode = currentRealTimeMap.get(DeviceCommandEnum.OPERATE_MODE.getCode());
        operateMode = ObjectUtils.isEmpty(operateMode) ? OperateModeEnum.UNIFORM_SPEED.getCode() : operateMode;
        if (OperateModeEnum.UNIFORM_SPEED.getCode().equals(operateMode)){
            // 当前流量
            switch (ledScreenContent) {
                case CURRENT:
                    result = toResult(currentRealTimeMap.get(DeviceCommandEnum.CURRENT_FLOW.getCode()),ledScreenContent);
                    break;
                case SPEED:
                    result = toResult(currentRealTimeMap.get(DeviceCommandEnum.UNIFORM_SPEED.getCode()),ledScreenContent);
                    break;
                default:
            }
        }else {
            // 当前流量
            switch (ledScreenContent) {
                case CURRENT:
                    List<HistoryData> flowData = getTimeSlotData(deviceId, DeviceCommandEnum.CUMULATIVE_FLOW.getCode(), 2);
                    BigDecimal currentData = getCurrentData(flowData);
                    result = toResult(currentData.toString(),ledScreenContent);
                    break;
                case SPEED:
                    result = toResult(currentRealTimeMap.get(DeviceCommandEnum.VARIABLE_SPEED.getCode()),ledScreenContent);
                    break;
                default:
            }
        }
        return result;
    }

    private String handleClassInfo(LedScreenContent ledScreenContent,Integer deviceId) {
        String result= "";
        String date = DateUtil.today();
        List<SysDictData> shiftTimeList = DictUtils.getDictCache("everyday_shift_time");
        for (SysDictData data : shiftTimeList) {
            if (ledScreenContent.getInfo().equals(data.getDictLabel())) {
                String[] timeArray = data.getDictValue().split(",");
                DateTime startTime,endTime;
                if (timeArray[0].startsWith("-")) {
                    startTime = SpecificDateUtil.getMsCurrentClassTime(date,timeArray[0]);
                } else {
                    startTime = SpecificDateUtil.getMsNextClassTime(date,timeArray[0]);
                }
                if (timeArray[1].startsWith("-")) {
                    endTime = SpecificDateUtil.getMsCurrentClassTime(date,timeArray[1]);
                } else {
                    endTime = SpecificDateUtil.getMsNextClassTime(date,timeArray[1]);
                }
                List<HistoryData> currentClassFlowData = getMsTimeSlotList(deviceId, DeviceCommandEnum.CUMULATIVE_FLOW.getCode(), startTime, endTime);
                result = getHistoryData(currentClassFlowData);
                result = toResult(result,ledScreenContent);
                currentClassFlowData.clear();
                break;
            }
        }
        return result;
    }

    private String getResultByContent(Integer deviceId,LedScreenContent ledScreenContent){
        String result = "";
        String date = DateUtil.today();
        DateTime current = null;
        switch (ledScreenContent) {
            case DAY:
                current = SpecificDateUtil.getMsCurrentDay(date);
                break;
            case MONTH:
                current = SpecificDateUtil.getMsCurrentMonth(date);
                break;
            case YEAR:
                current = SpecificDateUtil.getMsCurrentYear(date);
            case ALL:
                break;
            default:
        }
        List<HistoryData> flowList = getMsTimeSlotList(deviceId, DeviceCommandEnum.CUMULATIVE_FLOW.getCode(), current);
        result = getHistoryData(flowList);
        result = toResult(result,ledScreenContent);
        return result;
    }

    private BigDecimal getCurrentData(List<HistoryData> flowData) {
        //变速计算两条时间间隔计算数据
        BigDecimal currentData = new BigDecimal("0.00");
        if (flowData.size() == 2){
            Date lastTime = flowData.get(0).getCreateTime();
            Date firstTime = flowData.get(1).getCreateTime();
            long betweenTime  = DateUtil.between(lastTime, firstTime , DateUnit.MS);
            BigDecimal intervalTime = new BigDecimal(betweenTime).divide(new BigDecimal(1000),2, RoundingMode.HALF_UP);

            BigDecimal lastData = new BigDecimal(flowData.get(0).getDataValue());
            BigDecimal firstData = new BigDecimal(flowData.get(1).getDataValue());
            if (intervalTime.compareTo(new BigDecimal("0")) > 0 && lastData.compareTo(firstData) >= 0){
                // todo 保留五位小数
                currentData = lastData.subtract(firstData);
                currentData = currentData.divide(intervalTime,5, RoundingMode.HALF_UP).multiply(new BigDecimal("3600"));
            }
        }
        return currentData;
    }

    private List<HistoryData> getTimeSlotData(Integer deviceId, int dictValue, int limit){
        Criteria criteria = new Criteria();
        criteria.andOperator(
                Criteria.where("deviceId").is(deviceId),
                Criteria.where("dictValue").is(dictValue)
        );
        Sort ascSort = Sort.by(Sort.Order.desc("createTime"));
        if (limit > 0){
            return mongoComponent.selectList(criteria, limit, ascSort, HistoryData.class);
        } else {
            return mongoComponent.selectList(criteria, ascSort, HistoryData.class);
        }
    }

    private List<HistoryData> getMsTimeSlotList(Integer deviceId, int dictValue, DateTime currentDay){
        Criteria criteria = new Criteria();
        if (ObjUtil.isNotEmpty(currentDay)){
            criteria.andOperator(
                    Criteria.where("deviceId").is(deviceId),
                    Criteria.where("dictValue").is(dictValue),
                    Criteria.where("createTime").gte(currentDay)
            );
        } else {
            criteria.andOperator(
                    Criteria.where("deviceId").is(deviceId),
                    Criteria.where("dictValue").is(dictValue)
            );
        }
        return getTimeHistoryData(criteria);
    }

    private List<HistoryData> getMsTimeSlotList(Integer deviceId, int dictValue, DateTime startTime,DateTime endTime){
        Criteria criteria = new Criteria();
        criteria.andOperator(
                Criteria.where("deviceId").is(deviceId),
                Criteria.where("dictValue").is(dictValue),
                Criteria.where("createTime").gte(startTime),
                Criteria.where("createTime").lte(endTime)
        );
        return getTimeHistoryData(criteria);
    }

    private List<HistoryData> getTimeHistoryData(Criteria criteria) {
        List<HistoryData> result = new ArrayList<>();
        Sort ascSort = Sort.by(Sort.Order.asc("createTime"));
        Sort descSort = Sort.by(Sort.Order.desc("createTime"));
        List<HistoryData> startList = mongoComponent.selectList(criteria, 1, ascSort, HistoryData.class);
        if (CollUtil.isNotEmpty(startList)){
            result.add(startList.get(0));
        }
        List<HistoryData> endList = mongoComponent.selectList(criteria, 1, descSort, HistoryData.class);
        if (CollUtil.isNotEmpty(endList)){
            result.add(endList.get(0));
        }
        return result;
    }

    private String getHistoryData(List<HistoryData> dataList){
        BigDecimal result = new BigDecimal("0.00");
        BigDecimal zero = new BigDecimal("0.00");
        if (CollUtil.isNotEmpty(dataList)) {
            HistoryData maxhistoryData = dataList.get(dataList.size() - 1);
            HistoryData minhistoryData = dataList.get(0);
            BigDecimal max = new BigDecimal(maxhistoryData.getDataValue());
            BigDecimal min = new BigDecimal(minhistoryData.getDataValue());
            result = max.subtract(min);
            result = result.compareTo(zero) < 0 ? zero : result;
        }
        return result.toString();
    }

    private List<Integer> buildDictValues() {
        List<Integer> dictValues = new ArrayList<>();
        dictValues.add(DeviceCommandEnum.CURRENT_FLOW.getCode());
        dictValues.add(DeviceCommandEnum.CUMULATIVE_FLOW.getCode());
        dictValues.add(DeviceCommandEnum.OPERATE_MODE.getCode());
        dictValues.add(DeviceCommandEnum.VARIABLE_SPEED.getCode());
        dictValues.add(DeviceCommandEnum.UNIFORM_SPEED.getCode());
        return dictValues;
    }

    /**
     * 大于一万转换成万单位，保留两位小数
     * @param data 传入数据
     * @return 结果
     */
    public static String toResult(String data,LedScreenContent ledScreenContent){
        if (StringUtils.isEmpty(data)){
            return LedScreenContent.getTextInfo(ledScreenContent.getInfo()
                    ,"0.00",ledScreenContent.getUnit());
        }
        BigDecimal dataBigDecimal = new BigDecimal(data);
        BigDecimal tenThousands = new BigDecimal("10000");
        String unit = "万";
        if (dataBigDecimal.compareTo(tenThousands) >= 0) {
            dataBigDecimal = dataBigDecimal.divide(tenThousands,2, RoundingMode.HALF_UP);
            unit = unit + ledScreenContent.getUnit();
        } else {
            // todo 保留两位小数
            dataBigDecimal = dataBigDecimal.setScale(2, RoundingMode.HALF_UP);
            unit = ledScreenContent.getUnit();
        }
        return LedScreenContent.getTextInfo(ledScreenContent.getInfo()
                ,dataBigDecimal.toString(),unit);
    }

}
